home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Atari Mega Archive 1
/
Atari Mega Archive - Volume 1.iso
/
archiver
/
zoo21src.zoo
/
lzd.c
< prev
next >
Wrap
C/C++ Source or Header
|
1991-07-24
|
31KB
|
852 lines
#ifndef LINT
static char sccsid[]="@(#) lzd.c 2.6 88/01/30 20:39:18";
#endif /* LINT */
/*********************************************************************/
/* This file contains two versions of the lzd() decompression routine.
The default is to use a fast version coded by Ray Gardner. If the
symbol SLOW_LZD is defined, the older slower one is used. I have tested
Ray's code and it seems to be portable and reliable. But if you
suspect any problems you can define SLOW_LZD for your system in
options.h and cause the older code to be used. --R.D. */
/*********************************************************************/
#include "options.h"
#include "zoo.h"
#include "zooio.h"
#include "various.h"
#include "zoofns.h" /* function definitions */
#include "zoomem.h"
#include "debug.h"
#include "assert.h"
#include "lzconst.h"
#ifndef SLOW_LZD
/* Extensive modifications for speed by Ray Gardner
** Public domain by Raymond D. Gardner 9/26/88
**
** I apologize for the comments being so dense in places as to impair
** readability, but some of the stuff isn't very obvious and needs
** some explaining. I am also sorry for the messy control structure
** (quite a few labels and goto's) and very long lzd() function, but
** I don't know how to do this any other way without loss of speed.
**
** Ray Gardner
** 6374 S. Monaco Ct.
** Englewood, CO 80111
*/
#ifdef ANSI_HDRS
# include <string.h> /* to get memcpy */
#else
VOIDPTR memcpy();
#endif
#define STACKSIZE 4000 /* allows for about 8Mb string in worst case? */
/* stack grows backwards in this version, using pointers, not counters */
static char *stack;
static char *stack_pointer;
static char *stack_lim;
void init_dtab PARMS((void));
unsigned rd_dcode PARMS((void));
/* void wr_dchar (char); */ /* now a macro */
void ad_dcode PARMS((void));
#ifdef FILTER
/* to send data back to zoofilt */
extern unsigned int filt_lzd_word;
#endif /* FILTER */
#ifndef __GNUC__
void xwr_dchar PARMS ((char));
#else
void xwr_dchar PARMS ((int));
#endif /* __GNUC__ */
static int firstchar PARMS ((int));
static void cbfill PARMS ((void));
/* wr_dchar() is a macro for speed */
#define wr_dchar(c) { \
if (outbufp<outbuflim) \
*outbufp++=(c); \
else \
xwr_dchar(c); \
}
extern char *out_buf_adr; /* output buffer */
extern char *in_buf_adr; /* input buffer */
/* use pointers (not counters) for buffer (for speed) */
static char *outbufp; /* output buffer pointer */
static char *outbuflim; /* output buffer limit */
static char *outbufguard; /* output buffer "guard" */
char memflag = 0; /* memory allocated? flag */
int *head; /* lzw prefix codes */
char *tail; /* lzw suffix codes */
static unsigned cur_code;
static unsigned old_code;
static unsigned in_code;
static unsigned free_code;
static int nbits;
static unsigned max_code;
/* We use a buffer of codes to avoid a function call to unpack each
** one as needed. We allocate an extra slot past the end of the buffer
** and put a CLEAR code in it, to serve as a sentinel. This way we can
** fold the test for code buffer runout into the test for a clear code
** and avoid having an extra test on each code processed. Also, we don't
** always use the code buffer. We can only use it when the input buffer
** is at a byte boundary, and when we know that the codesize won't change
** before we fill the code buffer, and when we know we won't run out of
** bytes in the input buffer before filling the code buffer. So we start
** with the code buffer pointer pointing to the sentinel, and we always
** have it pointing at the sentinel when we can't (for one reason or
** another) be getting our codes from the code buffer. We check for this
** condition whenever we get a CLEAR code, and if so, we get the code
** via the good old rd_dcode() routine.
**
** One other problem with the code buffer approach is that we might get
** a CLEAR code in the middle of the buffer. This means that the next
** code is only 9 bits, but we have probably already unpacked a number of
** larger codes from the input into the buffer before we discover this.
** So we remember where (in the input buffer) the code buffer was filled
** from, and when a CLEAR code is encountered in the buffer (not the
** sentinel at the end) we back up the bit_offset pointer in the input
** buffer, and reset things to start unpacking the 9-bit codes from there.
*/
#define CODEBUF_SIZE 64 /* must be multiple of 8, experiment for best */
static unsigned codebuf[CODEBUF_SIZE+1]; /* code buffer */
static unsigned *codebufp; /* code buffer pointer */
static unsigned *codebuflim; /* code buffer limit */
/* bit offset within the input buffer of where the code buffer began */
static unsigned codebufoffset;
static unsigned masks[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
0x1ff, 0x3ff, 0x7ff, 0xfff, 0x1fff };
static unsigned bit_offset; /* note this only allows max 8K input buffer!!*/
#ifdef UNBUF_IO
#define BLOCKFILE int
#define BLOCKREAD read
#define BLOCKWRITE blockwrite
int read PARMS ((int, VOIDPTR, unsigned));
int write PARMS ((int, VOIDPTR, unsigned));
int blockwrite PARMS ((int, VOIDPTR, unsigned));
#else
#define BLOCKFILE ZOOFILE
#define BLOCKREAD zooread
#define BLOCKWRITE zoowrite
#endif /* UNBUF_IO */
static BLOCKFILE in_f, out_f;
/* rd_dcode() reads a code from the input (compressed) file and returns
its value. */
unsigned rd_dcode()
{
register char *ptra, *ptrb; /* miscellaneous pointers */
unsigned word; /* first 16 bits in buffer */
unsigned byte_offset;
char nextch; /* next 8 bits in buffer */
unsigned ofs_inbyte; /* offset within byte */
ofs_inbyte = bit_offset % 8;
byte_offset = bit_offset / 8;
bit_offset = bit_offset + nbits;
assert(nbits >= 9 && nbits <= 13);
if (byte_offset >= INBUFSIZ - 5) {
int space_left;
assert(byte_offset >= INBUFSIZ - 5);
debug((printf ("lzd: byte_offset near end of buffer\n")))
bit_offset = ofs_inbyte + nbits;
space_left = INBUFSIZ - byte_offset;
ptrb = byte_offset + in_buf_adr; /* point to char */
ptra = in_buf_adr;
/* we now move the remaining characters down buffer beginning */
debug((printf ("rd_dcode: space_left = %d\n", space_left)))
while (space_left > 0) {
*ptra++ = *ptrb++;
space_left--;
}
assert(ptra - in_buf_adr == ptrb - (in_buf_adr + byte_offset));
assert(space_left == 0);
if (BLOCKREAD (in_f, ptra, byte_offset) == -1)
prterror ('f', "I/O error in lzd:rd_dcode.\n");
byte_offset = 0;
}
ptra = byte_offset + in_buf_adr;
/* NOTE: "word = *((int *) ptra)" would not be independent of byte order. */
word = (unsigned char) *ptra; ptra++;
word = word | ( ((unsigned char) *ptra) << 8 ); ptra++;
nextch = *ptra;
if (ofs_inbyte != 0) {
/* shift nextch right by ofs_inbyte bits */
/* and shift those bits right into word; */
word = (word >> ofs_inbyte) | (((unsigned)nextch) << (16-ofs_inbyte));
}
return (word & masks[nbits]);
} /* rd_dcode() */
void init_dtab()
{
nbits = 9;
max_code = 512;
free_code = FIRST_FREE;
}
/* By making wr_dchar() a macro and calling this routine only on buffer
** full condition, we save a lot of function call overhead.
** We also use pointers instead of counters for efficiency (in the macro).
*/
void xwr_dchar (ch)
char ch;
{
if (outbufp >= outbuflim) { /* if buffer full */
if (BLOCKWRITE (out_f, out_buf_adr, (outbufp - out_buf_adr))
!= out